﻿using System;

namespace Course1
{
    /// 一、学习object
    /// 1.所有类都直接或者间接实现了object
    /// 2.子类重写object
    /// 3.相等性判定
    /// 4.指针相等性和对象相等性
    /// 5.为什么重写equals要重写hashcode？
    ///     重写equest必须重写hashcode
    ///     hashcode在hash结构的时候有用，如果两个的像的equals为true，那么hashcode必须相等。
    ///     hash表添加元素的过程，首先hash表是由一个数组+链表构成（不是绝对），hashcode默认继承自object，只要不是同一个堆内存，
    ///     hashcode就不一样.
    ///     假设我们改写了Student的equals，只要两个对象的Name字段相等就认为这两个对象相等（注意对象的equal相等只表示是人为意义的同一个对象。不是计算机认为的同一个对象，不是同一个内存）
    ///         第一次 添加元素new Student(name:"A")时，先计算A的hahscode，然后更具桶的长度取余（散列），得到这个桶0的链表，然后判断这个链表里是否存在（调用equals）
    ///         不存在则插入，否则替换。
    ///         第二次添加new Student(name:"A")时，由于没有重写hashcode，而默认hashcode，是根据对象的内存地址来的，而我们new了两次，所以第二次的hashcode可能和第一次的
    ///         不一样，取余的时候，可能放到桶1里去了，而桶1里面已经有了一个name为A的元素，此时就会导致链表违背我们定义equals相等性，hash表要求不能有重复的元素。
    ///  6. equals和==问题
    ///     如果没有重载==运算符，则==比较两个对象的内存地址
    ///     如果重写了==运算符，一般通过equals进行重写，此时==和equals的功能相同。
    ///     如果一个类型重写了==，但是这个类型的两个对象的引用类型是object的化，==比较内存地址（运算符重载之和编译时类型有关）
    ///  7.重写equals
    ///     必须同时重写hashcode，不然将违背hash存储的规则，使得hash表中存在两个equals为true的对象（即会发生重复）。
    /// 6.运算符重载
    /// 二、值类型和包装类型
    ///     int->int?
    ///     注意c#的基本类型采用的是结构体（结构体也是基本类型）
    /// 常量池问题
    ///     字符串常量存在常量池
    ///     string s1 = "123";
    ///     string s2 = "123";
    ///     var flag = ReferenceEqueals(s1,s2);返回true，地址相同
    /// 三、空指针异常
    ///     ?.运算符
    ///     string str = null;
    ///     str.ToString();会引发异常
    ///     str?.ToString();不会执行，只有当str不是null的时候才执行
    /// 四、string
    ///     了解：string和String
    ///     了解string和StringBuilder
    ///     特殊的类型,可以不用new的class(C#有{隐式|显示}转换的概念)。
    /// 常量池的问题。
    /// 五、运算符重载
    /// 六、数组
    ///    注意 object list = new object[20];问题，object也是object[]的父类
    /// 七、索引器
    /// 八、泛型
    ///     注意java里的泛型的假泛型，本质还是object
    ///     C#的泛型是真泛型，就是真的会把T用你穿过来的类型生成一个新的类模板
    /// 九、显式（强制）类型转换和隐式（自动）类型转换
    ///     强制类型转换的前提-存在继承关系
    ///     隐式发生在基本类型中
    ///     自定义隐式类型转换和显示类型转换规则
    ///     is:用于判断是否可以安全的进行强制类型转换，即是否存在继承关系
    ///     as:用于安全的进行强制类型转换
    /// 十、结构体 
    ///     结构体是值类型
    ///     结构体其他行为和class类型，可用实现接口，可以定义函数
    ///     注意结构转递问题，以及性能问题，可以使用ref进行优化
    /// 十一、枚举
    ///     本质是int，可以和int进行强制类型转换
    ///     枚举可以看做int的子集（列举部分int值）
    ///     使用枚举更加直观
    /// 
    internal class Example6 
    {
        public int Age;
        public int? Id = null;

        /// <summary>
        /// string
        /// </summary>
        public void TestString()
        {
            var str1 = "123";
            var str2 = "123";
            var str3 = new string("123");
            //比较内容，c#重写了==运算符
            Console.WriteLine(str1==str2);
            //比较指针的值
            Console.WriteLine(ReferenceEquals(str1,str2));//true,因为同一个字符串常量的内存地址相同

            Console.WriteLine(str2 == str3);
            //比较指针的值
            Console.WriteLine(ReferenceEquals(str2, str3));//false，new一定会返回一个新的对象（过程是从常量内存区，复制到堆内存）
        }

        /// <summary>
        /// array
        /// 所有的数组都派生自Array
        /// </summary>
        public void TestArray()
        {
            //定义数组
            int[] arr1 = new int[1];
            //定义数组并初始化，这时候可以省略长度
            int[] arr2 = new int[] { 0,2,0,12,89};
            Console.WriteLine(arr2.Length);//返回数组的长度
            //定义指针数组
            var students = new Student[20];
            students[0] = new Student();
        }
        /// <summary>
        /// 基本类型的包装类型
        /// 语法类型名?
        /// </summary>
        public void TestNullableType()
        {
            int? a1 = 2;
            int? a2 = null;
            int? a3 = 2;
            Console.WriteLine(ReferenceEquals(a1,a3));

        }
    }
    public class Student
    {
        private int[] list = new int[20];
        public int Id { get; set; }
        public string Name { get; set; }

        /// <summary>
        /// 索引器，用于在对象上检索（扩展）
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public int this[int index]
        {
            get 
            {
                return list[index];
            }
            set 
            {
                list[index] = value;
            }
        }
        public override bool Equals(object obj)
        {
            if (object.ReferenceEquals(obj, this))
            {
                return true;
            }
            if (obj is not Student)
            {
                return false;
            }
            var sutdent = (Student)obj;
            if (this.Id == sutdent.Id)
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// 哈希code（散列码）
        /// 如果两个对象的equest判定为true，则这两个对象的hahscode必须相同
        /// 不同对象的hashcode可以相同。
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return Id.GetHashCode();
        }

        /// <summary>
        /// 运算符重载
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static bool operator >(Student a, Student b)
        {
            return a.Id > b.Id;
        }
        /// <summary>
        /// 运算符重载
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static bool operator <(Student a, Student b)
        {
            return a.Id < b.Id;
        }
    }

    /// <summary>
    /// 非泛型
    /// 非泛型操作起来非常麻烦需要做强制类型转换
    /// </summary>
    public class ArrList
    {
        private object[] _list = new object[20];

        public object Get(int i)
        {
            return _list[i];
        }

        public void Set(int i, object data)
        {
            _list[i] = data;
        }
    }
    /// <summary>
    /// 泛型
    /// 真泛型，支持值类型，并且不会发生自动装箱和拆箱（java会有拆装箱的问题，因为java是假泛型，java泛型本质是object）
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class ArrList<T> //类泛型，整个类可用,<>用于声明泛型
        //where T : struct
        where T : class
    {
        private T[] _list = new T[20];
       
        public T Get(int i)
        {
            return _list[i];
        }

        public void Set(int i,T data)
        {
            _list[i] = data;
        }

        public E WW<E>(E e)
        {
            return e;
        }

        public void Test()
        {
            WW<string>("fff");//完整写法
            WW("fff");//自动推断，编译器会进行自动推断，除非无法推断
        }
    }
    /// <summary>
    /// 枚举本质的int，int的子集
    /// </summary>
    public enum Color
    {
        Red = 0,
        Green = 1,
        Blue = 2,
    }
    public class EnumTest
    {
        public void Test()
        {
            var color = Color.Blue;
            var color1 = (Color)1;//揭露本质
            var color3 = Enum.Parse<Color>("Blue");
        }
    }
    /// <summary>
    /// 结构体
    /// </summary>
    public struct Box
    {
        /// <summary>
        /// 可以定义字段
        /// </summary>
        private int x;
        /// <summary>
        /// 定义属性
        /// </summary>
        public int X { get { return x; } set{ x = value; } }
        /// <summary>
        /// 定义函数
        /// </summary>
        public void FF()
        { 
        }
    }

    public abstract class Person3
    {
        public int Age { get; set; }
        public string Name { get; set; }
    }
    /// <summary>
    /// 自定义{隐|显}式类型转换
    /// </summary>
    public class Student3 : Person3
    {
        /// <summary>
        /// 隐式转换：定义自动转换规则,因为自动系统不会转，那就告诉它该怎么转
        /// 本质就是自动创建过去了
        /// 不建议用，因为有点违背泛型，很少使用，推荐写一个转换函数，C#提供了更多的可能性
        /// </summary>
        /// <param name="d"></param>
        public static implicit operator Teacher3(Student3 d)
        {
            return new Teacher3() 
            {
                Name = d.Name,
                Age=d.Age
            };
        }
        public Teacher3 ToTeacher()
        {
            return new Teacher3()
            {
                Name = this.Name,
                Age = this.Age
            };
        }
        public static explicit operator Student3(Teacher3 d)
        {
            return new Student3()
            {
                Name = d.Name,
                Age = d.Age
            };
        }
    }
    public class Teacher3 : Person3
    {

    }

    public class TestConver
    {
        public void Test()
        {
            var p = new Student3();
            Teacher3 t = p;//自动转换,不建议使用
            Teacher3 t2 = p.ToTeacher();//建议，更加的直观
        }
    }
}
